if (SANITIZE OR NOT (ARCH_AMD64 OR ARCH_ARM) OR NOT (OS_LINUX OR OS_FREEBSD OR OS_DARWIN))
    if (ENABLE_JEMALLOC)
        message (${RECONFIGURE_MESSAGE_LEVEL}
                 "jemalloc is disabled implicitly: it doesn't work with sanitizers and can only be used with x86_64 or aarch64 on linux or freebsd.")
    endif()
    set (ENABLE_JEMALLOC OFF)
else()
    option (ENABLE_JEMALLOC "Enable jemalloc allocator" ${ENABLE_LIBRARIES})
endif ()

if (NOT ENABLE_JEMALLOC)
    if(USE_INTERNAL_JEMALLOC_LIBRARY)
        message (${RECONFIGURE_MESSAGE_LEVEL} "Can't use internal jemalloc with ENABLE_JEMALLOC=OFF")
    endif()

    add_library(jemalloc INTERFACE)
    target_compile_definitions(jemalloc INTERFACE USE_JEMALLOC=0)

    message (STATUS "Not using jemalloc")
    return()
endif ()

if (NOT OS_LINUX)
    message (WARNING "jemalloc support on non-linux is EXPERIMENTAL")
endif()

option (USE_INTERNAL_JEMALLOC_LIBRARY "Use internal jemalloc library" ${NOT_UNBUNDLED})

if (NOT USE_INTERNAL_JEMALLOC_LIBRARY)
    find_library(LIBRARY_JEMALLOC jemalloc)
    find_path(INCLUDE_JEMALLOC jemalloc/jemalloc.h)

    if (LIBRARY_JEMALLOC AND INCLUDE_JEMALLOC)
        set(EXTERNAL_JEMALLOC_LIBRARY_FOUND 1)

        set(THREADS_PREFER_PTHREAD_FLAG ON)
        find_package(Threads)

        set (CMAKE_REQUIRED_LIBRARIES ${LIBRARY_JEMALLOC} Threads::Threads "dl")
        set (CMAKE_REQUIRED_INCLUDES ${INCLUDE_JEMALLOC})
        check_cxx_source_compiles (
            "
            #include <jemalloc/jemalloc.h>

            int main() {
                free(mallocx(1, 0));
            }
            "
            EXTERNAL_JEMALLOC_LIBRARY_WORKS
        )

        if (EXTERNAL_JEMALLOC_LIBRARY_WORKS)
            add_library (jemalloc STATIC IMPORTED)
            set_property (TARGET jemalloc PROPERTY IMPORTED_LOCATION ${LIBRARY_JEMALLOC})
            set_property (TARGET jemalloc PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INCLUDE_JEMALLOC})
            set_property (TARGET jemalloc PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads dl)
        else()
            message (${RECONFIGURE_MESSAGE_LEVEL} "External jemalloc is unusable: ${LIBRARY_JEMALLOC} ${INCLUDE_JEMALLOC}")
        endif ()

    else()
        set(EXTERNAL_JEMALLOC_LIBRARY_FOUND 0)
        message (${RECONFIGURE_MESSAGE_LEVEL} "Can't find system jemalloc")
    endif()
endif ()

if (NOT EXTERNAL_JEMALLOC_LIBRARY_FOUND OR NOT EXTERNAL_JEMALLOC_LIBRARY_WORKS)
    set(USE_INTERNAL_JEMALLOC_LIBRARY 1)

    if (OS_LINUX)
        # ThreadPool select job randomly, and there can be some threads that had been
        # performed some memory heavy task before and will be inactive for some time,
        # but until it will became active again, the memory will not be freed since by
        # default each thread has it's own arena, but there should be not more then
        # 4*CPU arenas (see opt.nareans description).
        #
        # By enabling percpu_arena number of arenas limited to number of CPUs and hence
        # this problem should go away.
        #
        # muzzy_decay_ms -- use MADV_FREE when available on newer Linuxes, to
        # avoid spurious latencies and additional work associated with
        # MADV_DONTNEED. See
        # https://github.com/ClickHouse/ClickHouse/issues/11121 for motivation.
        set (JEMALLOC_CONFIG_MALLOC_CONF "percpu_arena:percpu,oversize_threshold:0,muzzy_decay_ms:10000")
    else()
        set (JEMALLOC_CONFIG_MALLOC_CONF "oversize_threshold:0,muzzy_decay_ms:10000")
    endif()
    # CACHE variable is empty, to allow changing defaults without necessity
    # to purge cache
    set (JEMALLOC_CONFIG_MALLOC_CONF_OVERRIDE "" CACHE STRING "Change default configuration string of JEMalloc" )
    if (JEMALLOC_CONFIG_MALLOC_CONF_OVERRIDE)
        set (JEMALLOC_CONFIG_MALLOC_CONF "${JEMALLOC_CONFIG_MALLOC_CONF_OVERRIDE}")
    endif()
    message (STATUS "jemalloc malloc_conf: ${JEMALLOC_CONFIG_MALLOC_CONF}")

    set (LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/jemalloc")

    set (SRCS
        ${LIBRARY_DIR}/src/arena.c
        ${LIBRARY_DIR}/src/background_thread.c
        ${LIBRARY_DIR}/src/base.c
        ${LIBRARY_DIR}/src/bin.c
        ${LIBRARY_DIR}/src/bitmap.c
        ${LIBRARY_DIR}/src/ckh.c
        ${LIBRARY_DIR}/src/ctl.c
        ${LIBRARY_DIR}/src/div.c
        ${LIBRARY_DIR}/src/extent.c
        ${LIBRARY_DIR}/src/extent_dss.c
        ${LIBRARY_DIR}/src/extent_mmap.c
        ${LIBRARY_DIR}/src/hash.c
        ${LIBRARY_DIR}/src/hook.c
        ${LIBRARY_DIR}/src/jemalloc.c
        ${LIBRARY_DIR}/src/large.c
        ${LIBRARY_DIR}/src/log.c
        ${LIBRARY_DIR}/src/malloc_io.c
        ${LIBRARY_DIR}/src/mutex.c
        ${LIBRARY_DIR}/src/mutex_pool.c
        ${LIBRARY_DIR}/src/nstime.c
        ${LIBRARY_DIR}/src/pages.c
        ${LIBRARY_DIR}/src/prng.c
        ${LIBRARY_DIR}/src/prof.c
        ${LIBRARY_DIR}/src/rtree.c
        ${LIBRARY_DIR}/src/sc.c
        ${LIBRARY_DIR}/src/stats.c
        ${LIBRARY_DIR}/src/sz.c
        ${LIBRARY_DIR}/src/tcache.c
        ${LIBRARY_DIR}/src/test_hooks.c
        ${LIBRARY_DIR}/src/ticker.c
        ${LIBRARY_DIR}/src/tsd.c
        ${LIBRARY_DIR}/src/witness.c
        ${LIBRARY_DIR}/src/safety_check.c
    )
    if (OS_DARWIN)
        list(APPEND SRCS ${LIBRARY_DIR}/src/zone.c)
    endif ()

    add_library(jemalloc ${SRCS})
    target_include_directories(jemalloc PRIVATE ${LIBRARY_DIR}/include)
    target_include_directories(jemalloc SYSTEM PUBLIC include)

    set (JEMALLOC_INCLUDE_PREFIX)
    # OS_
    if (OS_LINUX)
        set (JEMALLOC_INCLUDE_PREFIX "include_linux")
    elseif (OS_FREEBSD)
        set (JEMALLOC_INCLUDE_PREFIX "include_freebsd")
    elseif (OS_DARWIN)
        set (JEMALLOC_INCLUDE_PREFIX "include_darwin")
    else ()
        message (FATAL_ERROR "internal jemalloc: This OS is not supported")
    endif ()
    # ARCH_
    if (ARCH_AMD64)
        set(JEMALLOC_INCLUDE_PREFIX "${JEMALLOC_INCLUDE_PREFIX}_x86_64")
    elseif (ARCH_ARM)
        set(JEMALLOC_INCLUDE_PREFIX "${JEMALLOC_INCLUDE_PREFIX}_aarch64")
    else ()
        message (FATAL_ERROR "internal jemalloc: This arch is not supported")
    endif ()

    configure_file(${JEMALLOC_INCLUDE_PREFIX}/jemalloc/internal/jemalloc_internal_defs.h.in
        ${JEMALLOC_INCLUDE_PREFIX}/jemalloc/internal/jemalloc_internal_defs.h)
    target_include_directories(jemalloc SYSTEM PRIVATE
        ${CMAKE_CURRENT_BINARY_DIR}/${JEMALLOC_INCLUDE_PREFIX}/jemalloc/internal)

    target_compile_definitions(jemalloc PRIVATE -DJEMALLOC_NO_PRIVATE_NAMESPACE)

    if (CMAKE_BUILD_TYPE_UC STREQUAL "DEBUG")
        target_compile_definitions(jemalloc PRIVATE -DJEMALLOC_DEBUG=1 -DJEMALLOC_PROF=1)

        if (USE_UNWIND)
            target_compile_definitions (jemalloc PRIVATE -DJEMALLOC_PROF_LIBUNWIND=1)
            target_link_libraries (jemalloc PRIVATE unwind)
        endif ()
    endif ()

    target_compile_options(jemalloc PRIVATE -Wno-redundant-decls)
    # for RTLD_NEXT
    target_compile_options(jemalloc PRIVATE -D_GNU_SOURCE)

    set (USE_INTERNAL_JEMALLOC_LIBRARY 1)
endif ()

set_property(TARGET jemalloc APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS USE_JEMALLOC=1)
if (MAKE_STATIC_LIBRARIES)
    # To detect whether we need to register jemalloc for osx as default zone.
    set_property(TARGET jemalloc APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS BUNDLED_STATIC_JEMALLOC=1)
endif()

message (STATUS "Using jemalloc")
